/**
 * @file      file.h
 * @copyright Copyright (c) 2017, ZCW Co., Ltd. All Rights Reserved.
 * @brief     brief
 * @author    caiwang213@qq.com
 * @date      2017-11-25 21:54:17
 *
 * @note
 *  file.h defines
 */
#ifndef __FILE_H__
#define __FILE_H__

#include <string>
#include <list>
#include <stdio.h>

#ifndef WIN32
#include <unistd.h>
#include <fcntl.h>
#endif
namespace log4x
{
class File
{
public:
    File(): _file(NULL) {};
    ~File()
    {
        close();
    };

public:
    inline bool        isOpen();
    inline long        open(const char *path, const char * mod);
    inline void        clean(int index, int len);
    inline void        close();
    inline void        write(const char * data, size_t len);
    inline void        flush();

    inline long        size();
    inline long        ftell();
    inline long        fseek(int offset);

    inline std::string readLine();
    inline bool        removeFile(const std::string & path);
    inline std::string readContent();
private:
    FILE             * _file;
};

inline bool
File::isOpen()
{
    return _file != NULL;
}

inline long
File::open(const char *path, const char * mod)
{
    if (_file != NULL)
    {
        fclose(_file);
        _file = NULL;
    }

    _file = fopen(path, mod);
    if (_file)
    {
        long tel = 0;
        long cur = ::ftell(_file);
        ::fseek(_file, 0L, SEEK_END);
        tel = ::ftell(_file);
        ::fseek(_file, cur, SEEK_SET);

#ifndef _WIN32
        int fd = fileno(_file);
        int flags = fcntl(fd, F_GETFD, 0);
        if (flags < 0)
        {
            return -1;
        }

        fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
#endif
        return (tel < 0) ? 0 : tel;
    }

    return -1;
}

inline void
File::clean(int index, int len)
{
#if !defined(__APPLE__) && !defined(WIN32)
    if (_file != NULL)
    {
        int fd = fileno(_file);
#ifndef __ANDROID__
        posix_fadvise(fd, index, len, POSIX_FADV_DONTNEED);
#endif
        fsync(fd);
    }
#endif
}

inline void
File::close()
{
    if (_file != NULL)
    {
        clean(0, 0);
        flush();
        fclose(_file);
        _file = NULL;
    }
}

inline void
File::write(const char * data, size_t len)
{
    if (_file && len > 0)
    {
        if (fwrite(data, 1, len, _file) != len)
        {
            close();
        }
    }
}

inline void
File::flush()
{
    if (_file)
    {
        fflush(_file);
    }
}

inline long
File::size()
{
    if (!_file)
    {
        return -1;
    }

    long tel = 0;
    long cur = ::ftell(_file);
    ::fseek(_file, 0L, SEEK_END);
    tel = ::ftell(_file);
    ::fseek(_file, cur, SEEK_SET);

    return (tel < 0) ? 0 : tel;
}

inline long
File::ftell()
{
    if (!_file)
    {
        return -1;
    }

    return ::ftell(_file);
}

inline long
File::fseek(int offset)
{
    if (!_file)
    {

        return -1;
    }

    return ::fseek(_file, offset, SEEK_SET);
}

inline std::string
File::readLine()
{
    char buf[4 * 1024] = { 0 };
    if (_file && fgets(buf, sizeof(buf), _file) != NULL)
    {
        return std::string(buf);
    }
    return std::string();
}

inline bool
File::removeFile(const std::string & path)
{
    return ::remove(path.c_str()) == 0;
}

inline std::string
File::readContent()
{
    std::string content;

    if (!_file)
    {
        return content;
    }
    char buf[BUFSIZ];
    size_t ret = 0;
    do
    {
        ret = fread(buf, sizeof(char), BUFSIZ, _file);
        content.append(buf, ret);
    }
    while (ret == BUFSIZ);

    return content;
}
}
#endif
